﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using Osm.Map.Layers.Custom;
using Osm.Map.Layers.Tiles;
using Tools.Math.Geo;
using System.Threading;
using Osm.Routing.Route;
using Osm.Routing;
using Tools.Xml.Sources;
using Osm.Data;
using System.IO;
using Osm.Routing.Exceptions;
using Osm.Renderer.Gdi.Targets.UserControlTarget;
using Net_Trading;
using Demo.TspDemo.ServiceSSVG;
using System.Runtime.InteropServices;
namespace Demo.TspDemo
{
    public partial class fen_add_mission : Form
    {
        private CustomLayer _points_layer;

        private Osm.Routing.Route.Map.SpacialRouteLayer _route_layer;

        private CustomLayer _status_layer;

        private CustomLayer _resolved_layer;

        private List<GeoCoordinate> _points;

        private IDataSourceReadOnly _data;

        private GeoCoordinateBox _data_box;
        private List<RoutePointEntry> laRoute;

        private string _test_name;

        System.Windows.Forms.Timer tmr = new System.Windows.Forms.Timer();

        public fen_add_mission()
        {
            InitializeComponent();
        }
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            // create the map and all it's layers.
            Osm.Map.Map map = new Osm.Map.Map();
            map.Layers.Add(new TilesLayer());
            map.Layers.Add(new TilesLayer("http://tools.geofabrik.de/osmi/tiles/routing/{0}/{1}/{2}.png"));
            // map.Layers.Add(new TilesLayer("http://tools.geofabrik.de/osmi/tiles/geometry/{0}/{1}/{2}.png"));
            _status_layer =
                new CustomLayer();
            map.Layers.Add(_status_layer);
            _resolved_layer = new CustomLayer();
            map.Layers.Add(_resolved_layer);
            CustomLayer data_box_layer = new CustomLayer();
            map.Layers.Add(data_box_layer);
            _route_layer =
                new Osm.Routing.Route.Map.SpacialRouteLayer();
            map.Layers.Add(_route_layer);
            _points_layer =
                new CustomLayer();
            map.Layers.Add(_points_layer);

            // create the points list.
            _points = new List<GeoCoordinate>();

            // load points.
            _test_name = "ariana";
            // try to load a csv ;-seperated
            // latitude; longitude
            FileInfo csv = new FileInfo(string.Format("{0}.csv", _test_name));
            if (csv.Exists)
            {
                DataSet data =
                    Tools.Core.DelimitedFiles.DelimitedFileHandler.ReadDelimitedFile(null, csv, Tools.Core.DelimitedFiles.DelimiterType.DotCommaSeperated, true, false);
                foreach (DataRow row in data.Tables[0].Rows)
                {
                    // be carefull with the parsing and the number formatting for different cultures.
                    double latitude = double.Parse(row[7].ToString(), System.Globalization.CultureInfo.InvariantCulture);
                    double longitude = double.Parse(row[6].ToString(), System.Globalization.CultureInfo.InvariantCulture);

                    GeoCoordinate point = new GeoCoordinate(latitude, longitude);
                    _points.Add(point);

                    AddUiPoint(point);
                }
            }
            if (_points.Count > 1)
            {
                _data_box = new GeoCoordinateBox(_points.ToArray());
            }

            // create the data source.
            IDataSourceReadOnly osm_data = new Osm.Data.Xml.OsmSource.OsmDataSource(
                new Osm.Xml.OsmDocument(new XmlFileSource(string.Format("{0}.osm", _test_name))));
            osm_data =
                new Osm.Data.Cache.DataSourceCache(
                osm_data,
                13);
            Color box_color = Color.FromArgb(125, Color.Orange);
            if (osm_data.HasBoundinBox)
            {
                _data_box = osm_data.BoundingBox;
            }

            // show the data bouding box.
            List<GeoCoordinate> corners = new List<GeoCoordinate>();
            corners.Add(_data_box.Corners[3]);
            corners.Add(_data_box.Corners[1]);
            corners.Add(_data_box.Corners[0]);
            corners.Add(_data_box.Corners[2]);

            Osm.Map.Elements.ElementPolygon box_element = new Osm.Map.Elements.ElementPolygon(new Tools.Math.Shapes.ShapePolyGonF<GeoCoordinate, GeoCoordinateBox, GeoCoordinateLine>(
            Tools.Math.Geo.Factory.PrimitiveGeoFactory.Instance,
            corners.ToArray()),box_color.ToArgb(),10,false,true);
            data_box_layer.AddElement(box_element);
            _data = osm_data;

            // center and zoom!
            mapEditorUserControl1.Map = map;
            mapEditorUserControl1.Center = _data_box.Center;
            mapEditorUserControl1.ZoomFactor = 16;

            
            tmr.Interval = 100;
            tmr.Tick += new EventHandler(tmr_Tick);
            tmr.Enabled = true;
            tmr.Start();
        }
        private delegate void EmptyDelegate();

        void tmr_Tick(object sender, EventArgs e)
        {
            Invoke(new EmptyDelegate(RefreshMap));
        }

        private void RefreshMap()
        {
            mapEditorUserControl1.Refresh();
        }
        private void DoTspCalculation()
        {
            // do the TSP calculations on the given points.
            DoTspCalculation(_points, VehicleEnum.Car);
        }
        private void DoTspCalculation(List<GeoCoordinate> points, VehicleEnum vehicle)
        {
            int points_count = points.Count;

            _status_layer.Clear();

            // create a router and resolve the given points in the nodes list.
            Router router = Router.CreateFrom(_data);
            //router.RegisterProgressReporter(this);
            List<ResolvedPoint> nodes = router.Resolve(points);
            FileInfo csv = new FileInfo(string.Format("{0}.csv", _test_name));
            if (csv.Exists)
            {
                DataSet data =
                    Tools.Core.DelimitedFiles.DelimitedFileHandler.ReadDelimitedFile(null, csv, Tools.Core.DelimitedFiles.DelimiterType.DotCommaSeperated, true, false);
                DataTable original = data.Tables[0];

                for (int idx = 0; idx < nodes.Count; idx++)
                {
                    nodes[idx].Tags.Add(new KeyValuePair<string, string>("idx", idx.ToString()));
                    nodes[idx].Name = original.Rows[idx][0].ToString();
                }
            }

            // show the resolved points.
            _resolved_layer.Clear();
            foreach (ResolvedPoint point in nodes)
            {
                _resolved_layer.AddLine(point.Location, point.Original, false, 2, true, Color.Red.ToArgb());
            }

            // get the from/to points.
            ResolvedPoint from = nodes[0];
            ResolvedPoint to = nodes[0];
            nodes.RemoveAt(0);

            try
            {
                // calculate the TSP.
                //OsmSharpRoute result = router.Calculate(from, nodes[10]);
                OsmSharpRoute result = router.CalculateTsp(from, nodes, to);
                _status_layer.Clear();
                
                // write ouput.
                if (csv.Exists)
                {
                    DataSet data =
                        Tools.Core.DelimitedFiles.DelimitedFileHandler.ReadDelimitedFile(null, csv, Tools.Core.DelimitedFiles.DelimiterType.DotCommaSeperated, true, false);
                    DataTable original = data.Tables[0];
                    DataTable new_table = original.Clone();

                    foreach (RoutePointEntry entry in result.Entries)
                    {
                        if (entry.Points != null)
                        {
                            foreach (RoutePoint point in entry.Points)
                            {
                                string idx_str = RouteTags.GetValueFirst(point.Tags, "idx");
                                int idx = int.Parse(idx_str);
                                new_table.Rows.Add(original.Rows[idx].ItemArray);
                            }
                        }
                    }

                    FileInfo new_file = new FileInfo(csv.Name + ".out.csv");
                    Tools.Core.DelimitedFiles.DelimitedFileHandler.WriteDelimitedFile(null, new_table, new_file, Tools.Core.DelimitedFiles.DelimiterType.DotCommaSeperated, false);
                }
                result.SaveAsGpx(new FileInfo(csv.Name + ".out.gpx"));
                result.Save(new FileInfo(csv.Name + ".out.osm"));
                Singleton s = Singleton.Instance;
                s.Route = result;
              //  laRoute = result.Entries.ToList();
               // result.
                //digitalGauge1.Text = result.TotalDistance.ToString();
                // show the result.
                ShowRoute(_route_layer, result);
                //textEdit1.Text = laRoute.ElementAt(1).WayFromName.ToString();
               // MessageBox.Show(laRoute.ElementAt(1).WayFromName.ToString());

                //// generate instructions.
                //InstructionGenerator generator = new InstructionGenerator();
                //List<Instruction> instructions = generator.Generate(result);

                //// show the instructions.
                //this.Invoke(new InvokeParamDelegate(ShowInstructions), instructions);
            }
            catch (RouteNotFoundException ex)
            {
                // add to the error layer.
                _points_layer.Clear();

                //_status_layer.Clear();
                _status_layer.AddImage(global::Demo.TspDemo.Properties.Resources.flag_blue, ex.From.Location);
                foreach (ResolvedPoint point in ex.To)
                {
                    _status_layer.AddImage(global::Demo.TspDemo.Properties.Resources.flag_red, point.Location);
                }

                // invoke the cross-thread refresh.
                Invoke(new InvokeDelegate(Refresh));
            }

            if (points_count < _points.Count)
            {
                DoTspCalculation();
            }
        }
        public void ShowRoute(CustomLayer layer, OsmSharpRoute route)
        {
            // clear the routes layer and add it.
            _route_layer.Clear();
            _route_layer.AddRoute(route, Color.FromArgb(150, Color.Blue));

            // invoke the cross-thread refresh.
            Invoke(new InvokeDelegate(Refresh));
        }
        private delegate void InvokeDelegate();
        private void AddUiPoint(GeoCoordinate point)
        {
            _points_layer.AddImage(global::Demo.TspDemo.Properties.Resources.bluepin, point);

            Thread thr = new Thread(new ParameterizedThreadStart(CalculateTSP));
            thr.Start(_points.Count);
        }
        private delegate void CalculateTSPDelegate(int points);

        private void CalculateTSP(object points)
        {
            lock (_points)
            {
                this.CalculateTSP((int)points);
            }
        }

        private void CalculateTSP(int points)
        {
            if (points == _points.Count
                && points > 3)
            {
                DoTspCalculation();
            }
        }
        private void btnCalculate_Click(object sender, EventArgs e)
        {
            simpleButton1.Enabled = false;
            
            Thread thr = new Thread(new ThreadStart(DoTspCalculation));
            thr.Start();
        }
        private void mapEditorUserControl_MapClick(UserControlTargetEventArgs e)
        {
            if (_data_box.IsInside(e.Position))
            {
                AddUiPoint(e.Position);
                Refresh();
                _points.Add(e.Position);
            }
        }
        private void btnClear_Click(object sender, EventArgs e)
        {
            // clear everything.
            _points.Clear();
            _points_layer.Clear();
            _route_layer.Clear();
            _status_layer.Clear();
             tmr.Stop();
             simpleButton1.Enabled = true;
            // refresh map.
            Refresh();
        }

        private void fen_add_mission_FormClosed(object sender, FormClosedEventArgs e)
        {
            Singleton s = Singleton.Instance;
            s.Route = null;
            tmr.Stop();
        }

        private void simpleButton3_Click(object sender, EventArgs e)
        {
            Singleton s = Singleton.Instance;
            //var p = s.Route;
            Service1 serv = new Service1();
            var z = serv.AfficherListeChauffeur();
            var k = serv.AfficherListeVehicule();
            comboBox1.Items.Clear();
            foreach (var zz in z)
            {
                comboBox1.Items.Add(String.Format("{0} {1} {2}", zz.id_chauffeur, zz.Nom, zz.Prenom));
            }
            comboBox2.Items.Clear();
            foreach (var kk in k)
            {
                comboBox2.Items.Add(String.Format("{0} {1} {2}", kk.id_vehicule, kk.Matricule, kk.Marque));
            }
            panel_route.Visible = false;
            panel_2.Visible = true;
            textEdit6.Text = s.Route.Entries[1].WayFromName;
            textEdit5.Text = s.Route.Entries[s.Route.Entries.Length/2].WayFromName;
            textEdit3.Text = s.Route.TotalTime.ToString();
            textEdit7.Text = s.Route.TotalDistance.ToString();
            textEdit8.Text = ((s.Route.TotalDistance/1000) / s.Route.TotalTime).ToString();
            comboBox1.SelectedIndex = 1;
            comboBox2.SelectedIndex = 1;
            dateEdit1.DateTime = DateTime.Now;
            dateEdit2.DateTime = DateTime.Now;
            
        }

        private void simpleButton4_Click(object sender, EventArgs e)
        {
            Singleton s = Singleton.Instance;
            using (Service1 serv = new Service1())
            {
                var r = s.Route;
                Trajectoire traj = new Trajectoire { AdresseDebut = r.Entries[1].WayFromName, AdresseFin = r.Entries[r.Entries.Length/2].WayFromName, TempTotal = (float)r.TotalTime, DistanceTotal = (float)r.TotalDistance };
               
                //créer une liste des points
                List<ServiceSSVG.Point> listept = new List<ServiceSSVG.Point>();
                foreach (var xx in r.Entries)
                    listept.Add(new ServiceSSVG.Point { Lat = xx.Latitude, Lon = xx.Longitude });
              
                //enregistrement de la route
                bool r1 = serv.AjouterTrajectoire(traj);
              
                //enregistrement des points
                bool r2 = serv.AjouterListePoint(listept.ToArray());
             
                //enregistrement de la mission
                var machaine = String.Format("{0} {1}", dateEdit1.Text, timeEdit1.Text);
                var machaine2 = String.Format("{0} {1}", dateEdit2.Text, timeEdit2.Text);
                DateTime t1 = Convert.ToDateTime(machaine);
                DateTime t2 = Convert.ToDateTime(machaine2);
                bool r3 = serv.AjouterMission(textEdit1.Text, Convert.ToInt16(comboBox1.Text.Substring(0, comboBox1.Text.IndexOf(" "))),2,Convert.ToInt16(comboBox2.Text.Substring(0, comboBox2.Text.IndexOf(" "))), t1, t2);
                
                if ((r1 = true) && (r2 = true) && (r3 = true))
             
                    MessageBox.Show("Enregistrement effectué avec succès");
            }
        }

        private void fen_add_mission_Load(object sender, EventArgs e)
        {

        }

        private void simpleButton5_Click(object sender, EventArgs e)
        {
            tmr.Stop();
            Close();
        }
    }
}
